package tc.de.krawallmann.gravoblocks.game.model;// NOPMD

import java.util.ArrayList;
import java.util.List;

import org.newdawn.slick.Sound;

import tc.de.krawallmann.event.GameOverListener;
import tc.de.krawallmann.event.PauseListener;
import tc.de.krawallmann.event.ScoreListener;
import tc.de.krawallmann.extension.AchievementExtension;

/**
 * The Class AbstractTetrisGameRule.
 * 
 * @author KrawallMann
 * @version 1.0
 */
public abstract class AbstractTetrisGameRule implements ITetrisGameRule {
	/** The Constant WAIT_FOR_ROW. */
	protected static final int WAIT_FOR_ROW = 250;

	/** The Constant WAIT_FOR_START. */
	protected static final int WAIT_FOR_START = 300;

	/** The achievements. */
	private transient final AchievementExtension achievements;

	/** The cleared rows. */
	private int clearedRows;

	/** The falldown request. */
	private transient boolean falldownRequest;

	/** The fall key delay. */
	private transient int fallKeyDelay;

	/** The fall request. */
	private transient boolean fallRequest;

	/** The fall times. */
	private transient int fallTimes;

	/** The gameover. */
	private boolean gameover;

	/** The game over listeners. */
	private transient final List<GameOverListener> gameOverListeners;

	/** The hold request. */
	private transient boolean holdRequest;

	/** The left times. */
	private transient int leftTimes;

	/** The level. */
	private int level = 1;

	/** The move left key delay. */
	private transient int moveLeftKeyDelay;

	/** The move left request. */
	private transient boolean moveLeftRequest;

	/** The move right key delay. */
	private transient int moveRightKeyDelay;

	/** The move right request. */
	private transient boolean moveRightRequest;

	/** The pause listeners. */
	private transient final List<PauseListener> pauseListeners;

	/** The pause request. */
	private transient boolean pauseRequest;

	/** The right times. */
	private transient int rightTimes;

	/** The rotate left. */
	private transient boolean rotateLeft;

	/** The rotate right. */
	private transient boolean rotateRight;

	private transient final Sound rowSound;

	/** The score. */
	private int score;

	/** The score listeners. */
	private transient final List<ScoreListener> scoreListeners;

	/** The tetris. */
	private Tetris tetris;

	/**
	 * Instantiates a new.
	 * 
	 * @param achievements
	 *            the achievements
	 */
	public AbstractTetrisGameRule(final AchievementExtension achievements, final Sound row) {
		scoreListeners = new ArrayList<ScoreListener>();
		gameOverListeners = new ArrayList<GameOverListener>();
		pauseListeners = new ArrayList<PauseListener>();
		this.achievements = achievements;
		rowSound = row;
	}
	

	/**
	 * Gets the amount of full rows.
	 * 
	 * @param max
	 *            the maximum amount of fillable rows (=length of the longest
	 *            TetrisTile)
	 * @return the amount of full rows
	 */
	public abstract int getFullRowAmount(final int max);
	
	/**
	 * Removes the full rows.
	 * 
	 * @param grid
	 *            the grid
	 * @return the int
	 */
	public abstract int removeFullRows();
	

	/**
	 * Checks if the given row is row full.
	 * 
	 * @param row
	 *            the row to examine
	 * @return true, if this row is full
	 */
	public abstract boolean isRowFull(final int row);

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.htfzj#addGameOverListener(tc
	 * .de.krawallmann.event.GameOverListener)
	 */
	@Override
	public void addGameOverListener(final GameOverListener listener) {
		gameOverListeners.add(listener);
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.htfzj#addPauseListener(tc.de
	 * .krawallmann.event.PauseListener)
	 */
	@Override
	public void addPauseListener(final PauseListener listener) {
		pauseListeners.add(listener);
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.htfzj#addScoreListener(tc.de
	 * .krawallmann.event.ScoreListener)
	 */
	@Override
	public void addScoreListener(final ScoreListener listener) {
		scoreListeners.add(listener);
	}

	/**
	 * Clear game over listeners.
	 */
	@Override
	public void clearGameOverListeners() {
		gameOverListeners.clear();
	}

	/**
	 * Clear pause listeners.
	 */
	@Override
	public void clearPauseListeners() {
		pauseListeners.clear();
	}

	/**
	 * Clear score listeners.
	 */
	@Override
	public void clearScoreListeners() {
		scoreListeners.clear();
	}

	/**
	 * Gets the cleared rows.
	 * 
	 * @return the cleared rows
	 */
	@Override
	public int getClearedRows() {
		return clearedRows;
	}

	/**
	 * Gets the fall key delay.
	 * 
	 * @return the fall key delay
	 */
	public int getFallKeyDelay() {
		return fallKeyDelay;
	}

	/**
	 * Gets the fall times.
	 * 
	 * @return the fall times
	 */
	public int getFallTimes() {
		return fallTimes;
	}

	/**
	 * Gets the left times.
	 * 
	 * @return the left times
	 */
	public int getLeftTimes() {
		return leftTimes;
	}

	/**
	 * Gets the level.
	 * 
	 * @return the level
	 */
	@Override
	public int getLevel() {
		return level;
	}

	/**
	 * Gets the move left key delay.
	 * 
	 * @return the move left key delay
	 */
	public int getMoveLeftKeyDelay() {
		return moveLeftKeyDelay;
	}

	/**
	 * Gets the move right key delay.
	 * 
	 * @return the move right key delay
	 */
	public int getMoveRightKeyDelay() {
		return moveRightKeyDelay;
	}

	/**
	 * Gets the points.
	 * 
	 * @param rows
	 *            the rows
	 * @return the points
	 */
	public abstract int getPoints(int rows);

	/**
	 * Gets the right times.
	 * 
	 * @return the right times
	 */
	public int getRightTimes() {
		return rightTimes;
	}

	/**
	 * Gets the score.
	 * 
	 * @return the score
	 */
	@Override
	public int getScore() {
		return score;
	}

	/**
	 * Gets the tetris.
	 * 
	 * @return the tetris
	 */
	public Tetris getTetris() {
		return tetris;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isFallRequest()
	 */
	@Override
	public boolean isFallRequest() {
		return fallRequest;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isGameOver()
	 */
	@Override
	public boolean isGameOver() {
		return gameover;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isHoldRequest()
	 */
	@Override
	public boolean isHoldRequest() {
		return holdRequest;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isMoveLeftRequest
	 * ()
	 */
	@Override
	public boolean isMoveLeftRequest() {
		return moveLeftRequest;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isMoveRightRequest
	 * ()
	 */
	@Override
	public boolean isMoveRightRequest() {
		return moveRightRequest;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isPauseRequest()
	 */
	@Override
	public boolean isPauseRequest() {
		return pauseRequest;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isPauseRequest
	 * (boolean)
	 */
	@Override
	public boolean isPauseRequest(final boolean pauseRequest) {
		return this.pauseRequest;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isRotateLeft()
	 */
	@Override
	public boolean isRotateLeft() {
		return rotateLeft;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#isRotateRight()
	 */
	@Override
	public boolean isRotateRight() {
		return rotateRight;
	}

	/**
	 * Mark rows.
	 * 
	 * @param grid
	 *            the grid
	 */
	public abstract void markRows();

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.htfzj#removeGameOverListener
	 * (tc.de.krawallmann.event.GameOverListener)
	 */
	@Override
	public void removeGameOverListener(final GameOverListener listener) {
		gameOverListeners.remove(listener);
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.htfzj#removePauseListener(tc
	 * .de.krawallmann.event.PauseListener)
	 */
	@Override
	public void removePauseListener(final PauseListener listener) {
		pauseListeners.remove(listener);
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.htfzj#removeScoreListener(tc
	 * .de.krawallmann.event.ScoreListener)
	 */
	@Override
	public void removeScoreListener(final ScoreListener listener) {
		scoreListeners.remove(listener);
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#reset()
	 */
	@Override
	public void reset() {
		falldownRequest = false;
		clearedRows = 0;
		gameOverListeners.clear();
		score = 0;
		scoreListeners.clear();
		rightTimes = 0;
		leftTimes = 0;
		pauseRequest = false;
		pauseListeners.clear();
		fallTimes = 0;
		holdRequest = false;
		moveLeftKeyDelay = 140;
		level = 1;
		moveRightKeyDelay = 140;
		fallKeyDelay = 100;
		moveLeftRequest = false;
		moveRightRequest = false;
		rotateLeft = false;
		rotateRight = false;
		fallRequest = false;
		gameover = false;
	}

	/**
	 * Sets the cleared rows.
	 * 
	 * @param clearedRows
	 *            the new cleared rows
	 */
	public void setClearedRows(final int clearedRows) {
		this.clearedRows = clearedRows;
		for (final ScoreListener l : getScoreListeners()) {
			l.scoreUpdated(getLevel(), getScore(), getClearedRows());
		}
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#setFallDownRequest
	 * (boolean)
	 */
	@Override
	public void setFallDownRequest(final boolean falldownRequest) {
		this.falldownRequest = falldownRequest;
	}

	/**
	 * Sets the fall key delay.
	 * 
	 * @param fallKeyDelay
	 *            the new fall key delay
	 */
	@Override
	public void setFallKeyDelay(final int fallKeyDelay) {
		this.fallKeyDelay = fallKeyDelay;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#setFallRequest
	 * (boolean)
	 */
	@Override
	public void setFallRequest(final boolean fallRequest) {
		this.fallRequest = fallRequest;
	}

	/**
	 * Sets the fall times.
	 * 
	 * @param fallTimes
	 *            the new fall times
	 */
	@Override
	public void setFallTimes(final int fallTimes) {
		this.fallTimes = fallTimes;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#setHoldRequest
	 * (boolean)
	 */
	@Override
	public void setHoldRequest(final boolean holdRequest) {
		this.holdRequest = holdRequest;
	}

	/**
	 * Sets the left times.
	 * 
	 * @param leftTimes
	 *            the new left times
	 */
	@Override
	public void setLeftTimes(final int leftTimes) {
		this.leftTimes = leftTimes;
	}

	/**
	 * Sets the level.
	 * 
	 * @param level
	 *            the new level
	 */
	public void setLevel(final int level) {
		this.level = level;
		for (final ScoreListener l : getScoreListeners()) {
			l.scoreUpdated(getLevel(), getScore(), getClearedRows());
		}
	}

	/**
	 * Sets the move left key delay.
	 * 
	 * @param moveLeftKeyDelay
	 *            the new move left key delay
	 */
	@Override
	public void setMoveLeftKeyDelay(final int moveLeftKeyDelay) {
		this.moveLeftKeyDelay = moveLeftKeyDelay;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#setMoveLeftRequest
	 * (boolean)
	 */
	@Override
	public void setMoveLeftRequest(final boolean moveLeftRequest) {
		this.moveLeftRequest = moveLeftRequest;
	}

	/**
	 * Sets the move right key delay.
	 * 
	 * @param moveRightKeyDelay
	 *            the new move right key delay
	 */
	@Override
	public void setMoveRightKeyDelay(final int moveRightKeyDelay) {
		this.moveRightKeyDelay = moveRightKeyDelay;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#setMoveRightRequest
	 * (boolean)
	 */
	@Override
	public void setMoveRightRequest(final boolean moveRightRequest) {
		this.moveRightRequest = moveRightRequest;
	}

	/**
	 * Sets the pause request.
	 * 
	 * @param pauseRequest
	 *            the new pause request
	 */
	@Override
	public void setPauseRequest(final boolean pauseRequest) {
		this.pauseRequest = pauseRequest;
	}

	/**
	 * Sets the right times.
	 * 
	 * @param rightTimes
	 *            the new right times
	 */
	@Override
	public void setRightTimes(final int rightTimes) {
		this.rightTimes = rightTimes;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#setRotateLeft
	 * (boolean)
	 */
	@Override
	public void setRotateLeft(final boolean rotateLeft) {
		this.rotateLeft = rotateLeft;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.gravoblocks.game.model.ITetrisGameRule#setRotateRight
	 * (boolean)
	 */
	@Override
	public void setRotateRight(final boolean rotateRight) {
		this.rotateRight = rotateRight;
	}

	/**
	 * Sets the score.
	 * 
	 * @param score
	 *            the new score
	 */
	public void setScore(final int score) {
		this.score = score;
		for (final ScoreListener l : scoreListeners) {
			l.scoreUpdated(level, score, getClearedRows());
		}
	}

	/**
	 * Sets the tetris.
	 * 
	 * @param tetris
	 *            the new tetris
	 */
	public void setTetris(final Tetris tetris) {
		this.tetris = tetris;
	}

	/**
	 * Gets the achievements.
	 * 
	 * @return the achievements
	 */
	protected AchievementExtension getAchievements() {
		return achievements;
	}

	/**
	 * Gets the game over listeners.
	 * 
	 * @return the game over listeners
	 */
	protected List<GameOverListener> getGameOverListeners() {
		return gameOverListeners;
	}

	/**
	 * Gets the pause listeners.
	 * 
	 * @return the pause listeners
	 */
	protected List<PauseListener> getPauseListeners() {
		return pauseListeners;
	}

	protected Sound getRowSound() {
		return rowSound;
	}

	/**
	 * Gets the score listeners.
	 * 
	 * @return the score listeners
	 */
	protected List<ScoreListener> getScoreListeners() {
		return scoreListeners;
	}

	/**
	 * Checks if is falldown request.
	 * 
	 * @return true, if is falldown request
	 */
	protected boolean isFalldownRequest() {
		return falldownRequest;
	}

	/**
	 * Checks if is gameover.
	 * 
	 * @return true, if is gameover
	 */
	protected boolean isGameover() {
		return gameover;
	}

	/**
	 * Move ghost.
	 */
	protected void moveGhost() {
		tetris.getMainGrid().removeGhosts();
		tetris.getMainGrid().setTransparent(tetris.getMainGrid().getCurrentTile(), true);
		final TetrisTile ghostTile = tetris.getMainGrid().getCurrentTile().copy();
		boolean canMove = true;
		while (canMove) {
			canMove = tetris.getMainGrid().moveGhost(ghostTile, 0, 1);
		}
		tetris.getMainGrid().setGhost(ghostTile, true);
		tetris.getMainGrid().setTransparent(tetris.getMainGrid().getCurrentTile(), false);
	}

	/**
	 * Sets the falldown request.
	 * 
	 * @param falldownRequest
	 *            the new falldown request
	 */
	protected void setFalldownRequest(final boolean falldownRequest) {
		this.falldownRequest = falldownRequest;
	}

	/**
	 * Sets the gameover.
	 * 
	 * @param gameover
	 *            the new gameover
	 */
	protected void setGameover(final boolean gameover) {
		this.gameover = gameover;
	}

}
